---
title: "Adding New Observability Destination"
description: "There are tens if not hundreds of different observability destinations. Odigos's goal is to provide a seamless and easy way to ship observability data to any one of them."
sidebarTitle: 'Add Destinations'
icon: 'signal-stream'
---

In this guide, you will learn how to contribute a new destination to Odigos. We will create a new dummy destination called `mydest`.

Creating a new destination involves these steps:

1. Extending the UI for the new destination
2. Adding the collector configuration for the new destination
3. Generating the documentation for the new destination

### User Interface

For our new destination to be visible in the UI, we need to define the destination metadata and fields in YAML format:

<Steps>
  <Step title="Add Logo">
    Go to [`destinations/logos/`](https://github.com/odigos-io/odigos/tree/main/destinations/logos) directory and add your logo.
    - Must be in SVG format
    - Example: `mydest.svg`

    ```svg mydest.svg
    <svg fill="#F5F5F5" width="16" height="12" viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg">
      <title>My Destination</title>
      <g>
        <path d="M5.17215 9.01884L2.18377 7.69813C1.48159 7.38732 1.0448 6.73677 1.0448 5.99948C1.0448 5.2622 1.48056 4.61165 2.18377 4.30083L6.36172 2.45349L6.45156 2.41425V0H1.66127C1.0417 0 0.538818 0.4781 0.538818 1.06772V10.9312C0.538818 11.5209 1.0417 11.999 1.66127 11.999H5.83303H6.45259V9.5682L5.17319 9.01781H5.17215V9.01884Z"/>
        <path d="M14.3397 0H9.5484V2.4411L10.8041 2.98115L10.801 2.9698L13.8162 4.3029C14.5184 4.61372 14.9552 5.26426 14.9552 6.00155C14.9552 6.73883 14.5194 7.38938 13.8162 7.7002L10.1649 9.31417L9.54736 9.57646V12H14.3387C14.9583 12 15.4611 11.5219 15.4611 10.9323V1.06772C15.4611 0.4781 14.9583 0 14.3387 0H14.3397Z"/>
        <path d="M8.00048 8.47972C9.36972 8.47972 10.4808 7.36966 10.4808 5.99938C10.4808 4.6291 9.37076 3.51904 8.00048 3.51904C6.6302 3.51904 5.52014 4.6291 5.52014 5.99938C5.52014 7.36966 6.6302 8.47972 8.00048 8.47972Z"/>
      </g>
    </svg>
    ```
  </Step>
  <Step title="Add Destination Metadata">
    Go to [`destinations/data/`](https://github.com/odigos-io/odigos/tree/main/destinations/data) directory and create a new YAML file.
    - Example: `mydest.yaml`

    <AccordionGroup>
      <Accordion icon="circle-info" title="Allowed properties">
        | Key | Example | Required | Description | Docs |
        |---|---|:---:|---|:---:|
        | metadata.type | `mydest` | ✅ | unique identifier for the destination | ✅ |
        | metadata.displayName | `My Destination` | ✅ | display name of the destination | ✅ |
        | metadata.category | `managed` | ✅ | one-of: `managed` `self hosted` | ✅ |
        | spec.image | `mydest.svg` | ✅ | logo of the destination | ✅ |
        | spec.signals.traces.supported | `true` | ✅ | is tracing supported? | ✅ |
        | spec.signals.metrics.supported | `true` | ✅ | is metrics supported? | ✅ |
        | spec.signals.logs.supported | `false` | ✅ | is logging supported? | ✅ |
        | spec.note.type | `Note` | ❌ | type of [callout box](https://mintlify.com/docs/content/components/callouts) to display<br/><br/>one-of: `Note` `Info` `Warning` `Tip` `Check` | ✅ |
        | spec.note.content | `We handle the endpoint internally, so you don't need to provide it.` | ❌ | content of the callout box | ✅ |
        | spec.fields | - | ✅ | list of fields to be configured, [see specification here](#allowed-properties-for-spec-fields) | ✅ |
      </Accordion>
      <Accordion icon="circle-info" title="Allowed properties for spec.fields[]">
        | Key | Example | Required | Description | Docs |
        |---|---|:---:|---|:---:|
        | name | `MYDEST_API_KEY` | ✅ | key name of the field | ✅ |
        | displayName | `API Key` | ✅ | display name of the field | ✅ |
        | secret | `true` | ❌ | is the field a secret? (e.g. password, token, etc.) | ❌ |
        | initialValue | - | ❌ | default value for the field | ✅ |
        | componentType | `input` | ✅ | one-of: `input` `textarea` `dropdown` `checkbox` `multiInput` `keyValuePairs` | ✅ |
        | componentProps.type | `password` | ❌ | the type of value (e.g. string, number, password, etc.) | ✅ |
        | componentProps.required | `true` | ❌ | is the field required? | ✅ |
        | componentProps.values | - | ❌ | list of options for dropdown | ❌ |
        | componentProps.placeholder | `glc_eyJvIj...` | ❌ | placeholder text for input | ✅ |
        | componentProps.tooltip | `Obtained from your "My Dest" account` | ❌ | tooltip text for input | ✅ |
        | renderCondition | `["MYDEST_URL", "!=", ""]` | ❌ | hide/show the field in the UI (form only)<br/><br/>one-of: `[boolean]`<br/>`[string, operator, any]`<br/><br/>where `string` is the key name of the dependancy field, and `operator` is one-of: `!=` `==` `>=` `<=` `>` `<` | ❌ |
        | hideFromReadData | `["MYDEST_URL", "==", ""]` | ❌ | hide/show the field in the UI (read only)<br/><br/>one-of: `[boolean]`<br/>`[string, operator, any]`<br/><br/>where `string` is the key name of the dependancy field, and `operator` is one-of: `!=` `==` `>=` `<=` `>` `<` | ❌ |
        | customReadDataLabels | - | ❌ | custom labels for the UI (read only) | ❌ |
      </Accordion>
    </AccordionGroup>

    ```yaml mydest.yaml
    apiVersion: internal.odigos.io/v1beta1
    kind: Destination
    metadata:
      type: mydest
      displayName: My Destination
      category: managed
    spec:
      image: mydest.svg
      signals:
        traces:
          supported: true
        metrics:
          supported: true
        logs:
          supported: false
      fields:
        - name: MYDEST_URL
          displayName: URL
          componentType: input
          componentProps:
            required: true
        - name: MYDEST_REGION
          displayName: Region
          componentType: input
          componentProps:
            required: true
        - name: MYDEST_API_KEY
          displayName: API Key
          componentType: input
          componentProps:
            type: password
            required: true
          secret: true
    ```
  </Step>
</Steps>

### Collector Configuration

Now that our new vendor can be persisted/loaded in the Kubernetes data store, we need to implement the collector configuration.

<Steps>
  <Step title="Add Destination Type">
    Go to [`common/dests.go`](https://github.com/odigos-io/odigos/tree/main/common/dests.go) and add your new destination to the `DestinationType` enum.
    - Make sure the value is the same as the `type` property in `mydest.yaml`
  </Step>
  <Step title="Add Collector Config">
    Go to [`common/config`](https://github.com/odigos-io/odigos/tree/main/common/config) directory and create a new GO file.
    - Example: `mydest.go`

    <AccordionGroup>
      <Accordion icon="circle-info" title="Explain Config">
        - **DestType** - Returns the enum value of the destination added to [`common/dests.go`](https://github.com/odigos-io/odigos/tree/main/common/dests.go) in step 1.
        - **ModifyConfig** - Is called with the `dest` object which holds the data received from the UI and the `currentConfig` object. The `currentConfig` object contains the current configuration of the gateway collector. Modify this object to include the [OpenTelemetry configuration](https://opentelemetry.io/docs/collector/configuration/) needed by your destination. You can assume a basic configuration is already provided in the `currentConfig` object, for details see `getBasicConfig` method in [`common/config/root.go`](https://github.com/odigos-io/odigos/tree/main/common/config/root.go) file.
            - Make sure to give any exporter or pipeline a unique name in order to avoid conflicts, use the conventions:
                - `otlp/<dest-name>-<dest.GetID()>` for OTel gRPC exporters
                - `otlphttp/<dest-name>-<dest.GetID()>` for OTel HTTP exporters
                - `traces/<dest-name>-<dest.GetID()>` for traces pipelines
                - `metrics/<dest-name>-<dest.GetID()>` for metrics pipelines
                - `logs/<dest-name>-<dest.GetID()>` for logs pipelines
            - You can use the utility methods `isTracingEnabled`, `isMetricsEnabled` and `isLoggingEnabled` to determine which signals are selected by the user for the destination, and configure the collector accordingly.
      </Accordion>
    </AccordionGroup>

    ```go mydest.go
    package config

    import (
      "fmt"

      "github.com/odigos-io/odigos/common"
    )

    const (
      regionKey = "MYDEST_REGION"
      urlKey    = "MYDEST_URL"
      apiKeyKey = "MYDEST_API_KEY"
    )

    type MyDest struct{}

    func (m *MyDest) DestType() common.DestinationType {
      // DestinationType defined in common/dests.go
      return common.MyDestDestinationType
    }

    func (m *MyDest) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) ([]string, error) {
      config := dest.GetConfig()
      // To make sure that the exporter and pipeline names are unique, we'll need to define a unique ID
      uniqueUri := "mydest-" + dest.GetID()

      region, exists := config[regionKey]
      if !exists {
        region = "us"
        return errorMissingKey(regionKey)
      }

      endpoint, exists := config[urlKey]
      if !exists {
        endpoint = fmt.Sprintf("https://%s.mydest.com:4317", region)
      }

      // Modify the exporter here
      exporterName := "otlp/" + uniqueUri
      exporterConfig := GenericMap{
        "endpoint": endpoint,
        "headers": GenericMap{
          // The API Key is a secret, so we can't expose it in the config
          "x-mydest-header-apikey": "${MYDEST_API_KEY}",
        },
      }

      currentConfig.Exporters[exporterName] = exporterConfig

      // Modify the pipelines here
      var pipelineNames []string

      if isTracingEnabled(dest) {
        pipeName := "traces/" + uniqueUri
        currentConfig.Service.Pipelines[pipeName] = Pipeline{
          Exporters: []string{exporterName},
        }
        pipelineNames = append(pipelineNames, pipeName)
      }

      if isMetricsEnabled(dest) {
        pipeName := "metrics/" + uniqueUri
        currentConfig.Service.Pipelines[pipeName] = Pipeline{
          Exporters: []string{exporterName},
        }
        pipelineNames = append(pipelineNames, pipeName)
      }

      if isLoggingEnabled(dest) {
        pipeName := "logs/" + uniqueUri
        currentConfig.Service.Pipelines[pipeName] = Pipeline{
          Exporters: []string{exporterName},
        }
        pipelineNames = append(pipelineNames, pipeName)
      }

      return pipelineNames, nil
    }
    ```
  </Step>
  <Step title="Add Available Config">
    Go to [`common/config/root.go`](https://github.com/odigos-io/odigos/tree/main/common/config/root.go) and add the new destination to the `availableConfigers` list.
    - Example: `&MyDest{}`

    ```go common/config/root.go
    var availableConfigers = []Configer{
      &MyDest{},
      /* List of existing destinations...  */
    }
    ```
  </Step>
</Steps>

### Kubernetes-Specific Collector Configuration

If your destination configures Kubernetes settings, for example you have a config field that accepts a value that should be mounted
in the Collector Pod, create a `K8sConfiger` object in [`destinations.k8sconfig`](https://github.com/odigos-io/odigos/tree/main/autoscaler/k8sconfig).

This interface provides an additional function, `ModifyGatewayCollectorDeployment`, which accepts a Destination and Collector Deployment.
Your implementation can use this function to modify the Deployment as necessary based on the Destination config.

Note: It is the responsibility of your implementation to check for the validity of this config, such as checking for conflicts or values
that already exist.

The `K8sConfiger` interface also requires a `DestType()` function, similar to common Destinations. This function returns the type
of common Destination that this Kubernetes implementation relies on. This is used by the Autoscaler to conditionally apply the `K8sConfiger`
only for the matching Destination.

When it is implemented, add your `K8sConfiger` implementation to the `availableConfigers` list in
[`destinations/k8sconfig/root.go`](https://github.com/odigos-io/odigos/tree/main/autoscaler/k8sconfig/root.go)

### Generating Documentation

<Steps>
  <Step title="Install Python">
    Make sure you have [Python installed](https://www.python.org/downloads/) on your system.
  </Step>
  <Step title="Generate Documentation">
    Go to [`docs`](https://github.com/odigos-io/odigos/tree/main/docs) directory, and run:

    ```bash
    python sync-dest-doc.py
    # OR
    python3 sync-dest-doc.py
    ```
  </Step>
  <Step title="Edit Documentation">
    The documentation has been generated in the [`docs/backends`](https://github.com/odigos-io/odigos/tree/main/docs/backends) directory. Feel free to add any additional content to the generated file.
    - Edit only between the `!! START CUSTOM EDIT !!` and `!! END CUSTOM EDIT !!` comments
    - Example: `mydest.mdx`

    ```mdx mydest.mdx
    {/*
        !! Do not remove this comment, this acts as a key indicator in `docs/sync-dest-doc.py` !!
        !! START CUSTOM EDIT !!
    */}

    **Creating Account**<br />
    Go to the **[🔗 website](https://odigos.io) > Account** and click **Sign Up**

    **Obtaining Access Token**<br />
    Go to **⚙️ > Access Tokens** and click **Create New**

    {/*
        !! Do not remove this comment, this acts as a key indicator in `docs/sync-dest-doc.py` !!
        !! END CUSTOM EDIT !!
    */}
    ```
  </Step>
  <Step title="Update README.md">
    Go to [`README.md`](https://github.com/odigos-io/odigos/tree/main/README.md) and add your new destination to the list of destinations.
  </Step>
</Steps>

#### That's it! Now you can use your new destination in the UI and send data to it.

**Please submit a PR to the [odigos git repository](https://github.com/odigos-io/odigos).**
